package edu.gdpu.bytecode;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @author mazebin
 * @date 2021年 07月10日 00:08:00
 * 透过字节码分析Java动态代理机制
 *
 *
 * 源码分析一下newProxyInstance方法
 * public static Object newProxyInstance(ClassLoader loader,
 * Class<?>[] interfaces,
 * InvocationHandler h)
 * throws IllegalArgumentException
 * {
 * // 检查传入的InvocationHandler对象（即h）是否为null，即代理类对象
 * Objects.requireNonNull(h);
 * <p>
 * //克隆接口数组
 * final Class<?>[] intfs = interfaces.clone();
 * // 获取安全权限相关的对象实例
 * final SecurityManager sm = System.getSecurityManager();
 * if (sm != null) {
 * // 进行安全权限检查
 * checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
 * }
 * <p>
 * //获取生成的动态代理类
 * Class<?> cl = getProxyClass0(loader, intfs);
 * <p>
 * //获取生成的动态代理类的构造方法并且调用
 * try {
 * if (sm != null) {
 * // 权限检查
 * checkNewProxyPermission(Reflection.getCallerClass(), cl);
 * }
 * <p>
 * // private static final Class<?>[] constructorParams = { InvocationHandler.class };
 * // 通过反射获取生成的动态代理类的构造器，注意构造器的参数类型是InvocationHandler对象
 * final Constructor<?> cons = cl.getConstructor(constructorParams);
 * final InvocationHandler ih = h;
 * if (!Modifier.isPublic(cl.getModifiers())) {
 * AccessController.doPrivileged(new PrivilegedAction<Void>() {
 * public Void run() {
 * // 当构造器为私有时，防止不能创建对象
 * cons.setAccessible(true);
 * return null;
 * }
 * });
 * }
 * // 将代理类对象（即ds）传入并且返回动态代理对象
 * return cons.newInstance(new Object[]{h});
 * } catch (IllegalAccessException|InstantiationException e) {
 * throw new InternalError(e.toString(), e);
 * } catch (InvocationTargetException e) {
 * Throwable t = e.getCause();
 * if (t instanceof RuntimeException) {
 * throw (RuntimeException) t;
 * } else {
 * throw new InternalError(t.toString(), t);
 * }
 * } catch (NoSuchMethodException e) {
 * throw new InternalError(e.toString(), e);
 * }
 * }
 * 通过上面的分析可以看到newProxyInstance()方法主要完成这散步操作：
 * <p>
 * 调用getProxyClass0()方法创建动态代理类的class文件
 * 获取动态代理类参数类型为InvocationHandler的构造器
 * 给构造传入代理类对象并返回动态代理实例
 * 接下来我们分析一下虚拟机是如何在运行期间创建动态代理类的class文件？
 * private static Class<?> getProxyClass0(ClassLoader loader,
 * Class<?>... interfaces) {
 * if (interfaces.length > 65535) {
 * throw new IllegalArgumentException("interface limit exceeded");
 * }
 * <p>
 * // 如果给定的加载器加载的代理类已经实现了给定的接口，则从缓存中返回动态代理class对象
 * // 否则使用 ProxyClassFactory 创建代理类
 * return proxyClassCache.get(loader, interfaces);
 * }
 * private static final class ProxyClassFactory
 * implements BiFunction<ClassLoader, Class<?>[], Class<?>>
 * {
 * // 生成代理类类名的前缀为 $Proxy
 * private static final String proxyClassNamePrefix = "$Proxy";
 * <p>
 * // 生成唯一代理类名称的下一个数字
 * private static final AtomicLong nextUniqueNumber = new AtomicLong();
 * @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 * <p>
 * Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
 * for (Class<?> intf : interfaces) {
 * Class<?> interfaceClass = null;
 * try {
 * // 使用给定的类加载器加载给定的接口
 * interfaceClass = Class.forName(intf.getName(), false, loader);
 * } catch (ClassNotFoundException e) {
 * }
 * if (interfaceClass != intf) {
 * throw new IllegalArgumentException(
 * intf + " is not visible from class loader");
 * }
 * if (!interfaceClass.isInterface()) {
 * throw new IllegalArgumentException(
 * interfaceClass.getName() + " is not an interface");
 * }
 * if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
 * throw new IllegalArgumentException(
 * "repeated interface: " + interfaceClass.getName());
 * }
 * }
 * <p>
 * // 为开始生成class对象做一些准备工作
 * String proxyPkg = null;     // 用于定义代理类的包
 * int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
 * <p>
 * for (Class<?> intf : interfaces) {
 * int flags = intf.getModifiers();
 * if (!Modifier.isPublic(flags)) {
 * accessFlags = Modifier.FINAL;
 * String name = intf.getName();
 * int n = name.lastIndexOf('.');
 * String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
 * if (proxyPkg == null) {
 * proxyPkg = pkg;
 * } else if (!pkg.equals(proxyPkg)) {
 * throw new IllegalArgumentException(
 * "non-public interfaces from different packages");
 * }
 * }
 * }
 * <p>
 * if (proxyPkg == null) {
 * // if no non-public proxy interfaces, use com.sun.proxy package
 * proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
 * }
 * <p>
 *
 *  // 选择要生成的代理类的名称
 * <p>
 * long num=nextUniqueNumber.getAndIncrement();
 * String proxyName=proxyPkg+proxyClassNamePrefix+num;
 * <p>
 * // 生成指定的代理类
 * byte[]proxyClassFile=ProxyGenerator.generateProxyClass(
 * proxyName,interfaces,accessFlags);
 * try{
 * return defineClass0(loader,proxyName,
 * proxyClassFile,0,proxyClassFile.length);
 * }catch(ClassFormatError e){
 * throw new IllegalArgumentException(e.toString());
 * }
 * }
 * }
 *   获取要生成的代理类的class文件的字节流
 *               byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)
 *
 *        根据字节流调用C++或C创建class对象
 *               defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
 *
 *        获取字节流的实现如下所示：
 *        // var0 要生成代理类的全限定类名
 * // var1 目标类实现的接口
 * // var2 生成代理类的权限
 * public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
 *     ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
 *     final byte[] var4 = var3.generateClassFile();
 *
 *     // private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
 *     // 是否保存生成的class文件
 *     if (saveGeneratedFiles) {
 *         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 *             public Void run() {
 *                 try {
 *                     int var1 = var0.lastIndexOf(46);
 *                     Path var2;
 *                     if (var1 > 0) {
 *                         Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
 *                         Files.createDirectories(var3);
 *                         var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
 *                     } else {
 *                         var2 = Paths.get(var0 + ".class");
 *                     }
 *
 *                     Files.write(var2, var4, new OpenOption[0]);
 *                     return null;
 *                 } catch (IOException var4x) {
 *                     throw new InternalError("I/O exception saving generated file: " + var4x);
 *                 }
 *             }
 *         });
 *     }
 *
 *     return var4;
 * }
 * 在generateProxyClass()方法中，我们通过判断sun.misc.ProxyGenerator.saveGeneratedFiles属性是否为true，可以决定我们是否保留class文件。
 * 所以，我们将其改为true，在刚才的main方法下的第一行，添加如下代码：
 * System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
 * 就可以将生成的动态代理类保存在磁盘上了
 *
 */

public class DynamicProxyTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        RealSubject realSubject = new RealSubject();
        InvocationHandler subject = new DynamicSubject(realSubject);
        Class<? extends RealSubject> aClass = realSubject.getClass();
        Subject o = (Subject) Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), subject);
        o.request();
        System.out.println(o.getClass());
        System.out.println(o.getClass().getSuperclass());
    }
}

